package com.woniu.controller;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Snowflake;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.woniu.model.AllRoomnum;
import com.woniu.model.Message;
import com.woniu.utils.JwtUtil;
import com.woniu.utils.RedisKeyUtil;
import com.woniu.utils.RedisUtil;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;


import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * websocket消息推送
 */
@Slf4j
@Component
@ServerEndpoint("/webSocket/{token}/{roomNum}")
public class WebSocket {

    //private static RedisUtil redisUtil = SpringCtxUtils.getBean(RedisUtil.class);
    private static RedisUtil redisUtil;

    @Autowired
    public void setRedisUtil(RedisUtil redisUtil) {
        WebSocket.redisUtil = redisUtil;
    }


    public static  RabbitTemplate rabbitTemplate;
    @Autowired
    public void setRedisUtil(RabbitTemplate rabbitTemplate) {
        WebSocket.rabbitTemplate = rabbitTemplate;
    }

    //private Session session;
    /*
     * 用来记录房间及人数 string 的数据表示房间号 set的数据表示所有人的 session 可以通过这个 来推送消息
     */
    private static Map<String, Set> roomMap = new ConcurrentHashMap(8);
    /*
     * 用来记录session_id与用户id之间的关系
     */
    public static Map<String, Integer> userIdMap = new ConcurrentHashMap(8);
    /*
     * 将redis的json转为对象的工具
     */
    public static ObjectMapper objectMapper =new ObjectMapper();


    /**
     * 判断当前角色是否为 用户
     * @param token 该token 是前端传递过来的token
     * @return 是用户 返回true 否则 返回false
     */
    private boolean isUser(String token) throws Exception {
        String refreshToken = redisUtil.get(token);
        String role = null;
        try {
            role = JwtUtil.parseUserToken(refreshToken).get("role").toString();
        } catch (Exception e) {
            System.out.println("((((((((((((((((((((");
            System.out.println("我进来了");
            return false;
        }
        if (role==null||!role.equals("用户")){
            return false;
        }
        return true;
    }
    /**
     * 链接打开时
     * @param session
     * @throws Exception
     */
    @OnOpen
    public void open(Session session,  @PathParam("token") String token,  @PathParam("roomNum") String roomNum) throws Exception {
        System.out.println("***********************");
        String refreshToken = redisUtil.get(token);
        int accountId;
        if(isUser(token)){
            System.out.println("我是用户");
            accountId = Integer.parseInt(JwtUtil.parseUserToken(refreshToken).get("accountId").toString());
            Double zscore = 0d;
            //本次链接是用户的情况
            //判断用户是否已经聊过天 聊过天在reids取出房间号
            if (redisUtil.exist(RedisKeyUtil.userTalkRoomNum(accountId+""))) {
                roomNum = redisUtil.get(RedisKeyUtil.userTalkRoomNum(accountId + ""));
                //设置一个 用户上次聊天的最后一条数据 的score
                List<String> collect = redisUtil.zrevrange(RedisKeyUtil.talkRoomNum(roomNum), 0, -1).stream().limit(1).collect(Collectors.toList());
                if (collect!=null&&collect.size()!=0) {
                    //获取第一个成员(最大分数的成员)
                    zscore = redisUtil.zscore(RedisKeyUtil.talkRoomNum(roomNum), collect.get(0));
                }
            }else {
                //没有聊过 使用雪花算法生成房间号
                roomNum = new Snowflake(1,1).nextIdStr();

                redisUtil.set(RedisKeyUtil.userTalkRoomNum(accountId+""),roomNum);

                Claims claims = JwtUtil.parseUserToken(refreshToken);
                //记录新聊天房间号和对应用户信息(存入zset结构数据)
                HashMap<String, Object> map = new HashMap<>();
                map.put("roomNum",roomNum);
                map.put("accountId",accountId+"");
                map.put("username",claims.get("username")+"");
                map.put("avatarPath",claims.get("avatarPath")+"");

                redisUtil.zadd(RedisKeyUtil.allUserIdAndRoomNum(),objectMapper.writeValueAsString(map) ,0d);

                //新增的情况 消息队列存入数据库
                AllRoomnum allRoomnum = new AllRoomnum();
                allRoomnum.setRoomNum(roomNum);
                allRoomnum.setAvatarPath(claims.get("avatarPath")+"");
                allRoomnum.setUserId(accountId);
                allRoomnum.setUsername(claims.get("username")+"");
                Map<String, Object> objectMap = BeanUtil.beanToMap(allRoomnum);
                rabbitTemplate.convertAndSend("roomNum_queue",objectMap);
            }
            //设置 用户本次开始 聊天 的分数
            redisUtil.set(RedisKeyUtil.userIdStartTalkScore(accountId+""),zscore+"");
        }else {
            System.out.println("我是站长");
            System.out.println(refreshToken);
            try {
                accountId = Integer.parseInt(JwtUtil.parseStationBossToken(refreshToken).get("accountId").toString());
            } catch (Exception e) {
                throw new Exception("非法访问");
            }
            System.out.println(accountId);
            if (roomNum==null||roomNum.equals("")) throw new Exception("非法访问异常");
        }



        try {
            Set set = roomMap.get(roomNum);
            //将当前用户的id和正在使用的 session 对应起来
            userIdMap.put(session.getId(), accountId);
            // 如果是新的房间，则创建一个映射，如果房间已存在，则把用户放进去
            //set 表示这个房间号的所有人的 session的id
            if (set == null) {
                set = new CopyOnWriteArraySet();
                set.add(session);

                roomMap.put(roomNum, set);
                System.out.println(roomMap.get(roomNum));
            } else {
                //不为null 说明已经存在该房间 直接添加即可
                set.add(session);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        //如果存在 历史消息 立即加载 推送给未推送消息的用户
        //拿到该房间的 所有人的 sessions id
        Set<Session> sessions = roomMap.get(roomNum);

        for (Session s : sessions) {
            System.out.println("我是链接打开时的Session"+s);
            //当前session 的getId 和房间里面已有的session的id 相等 才推送
            //只推送 当前 访问该房间的用户
            if (s.getId()==session.getId()){
                if (redisUtil.exist(RedisKeyUtil.talkRoomNum(roomNum))){
                    //取出zest 的数据 (默认是从小到大的数据排序)
                    Set<String> zrange = redisUtil.zrange(RedisKeyUtil.talkRoomNum(roomNum));
                    for (String messages : zrange) {
                        //Message message = objectMapper.readValue(messages, Message.class);
                        System.out.println(messages);
                        s.getBasicRemote().sendText(messages);
                    }
                }
            }
        }

    }

    /**
     * 链接关闭时
     * @param session
     * @throws Exception
     */
    @OnClose
    public void onClose(Session session,@PathParam("token") String token,@PathParam("roomNum") String roomNum) throws Exception {
        if (isUser(token)) {
            roomNum = redisUtil.get(RedisKeyUtil.userTalkRoomNum(userIdMap.get(session.getId()) + ""));
            HashMap<String, String> map = new HashMap<>();
            map.put("userId",userIdMap.get(session.getId())+"");
            map.put("roomNum",roomNum);
            //将聊天数据存入数据库
            rabbitTemplate.convertAndSend("message_queue",map);
        }
        userIdMap.remove(session.getId());
        // 如果某个用户离开了，就移除相应的信息
        if (roomMap.containsKey(roomNum)) {
            roomMap.get(roomNum).remove(session);
        }
        //链接结束

    }

    @OnMessage
    public void reveiveMessage(Session session, String message,@PathParam("token") String token,@PathParam("roomNum") String roomNum) throws Exception {
        Claims claims = null;
        String refreshToken = redisUtil.get(token);
        if (isUser(token)){
            roomNum =  redisUtil.get(RedisKeyUtil.userTalkRoomNum(userIdMap.get(session.getId()) + ""));
            claims = JwtUtil.parseUserToken(refreshToken);
        }else{
            claims = JwtUtil.parseStationBossToken(refreshToken);
            if (roomNum==null||roomNum.equals("")) {
                throw new Exception("非法访问");
            }
        }

        log.info("接受到用户{}的数据:{}", session.getId(), message);

        System.out.println(message);

        /*
         * message 数据格式
         * time 可以用来做时间排序
         {"message":"3333","username":"3","userName":"吴彦祖","userImg":"../../static/img/headImg/mr.png","roleName":"用户","time":1713841404318}
         */
        Message messageModel = new Message();
        if (message!=null||!message.equals("")){
            //{"message":"我是站长"}
            Map<String, String> map = objectMapper.readValue(message, Map.class);
            messageModel.setMessage(map.get("message"));
            messageModel.setRoomNum(roomNum);
            messageModel.setCreateTime(System.currentTimeMillis()+"");
            messageModel.setAccountId((Integer) claims.get("accountId"));

            //存入redis 中
            //1.首先 判断 redis 中是否有这个key 没有 新增 有 累加
            if (redisUtil.exist(RedisKeyUtil.talkRoomNum(roomNum))){
                //存在的情况 累加
                List<String> collect = redisUtil.zrevrange(RedisKeyUtil.talkRoomNum(roomNum), 0, -1).stream().limit(1).collect(Collectors.toList());

                Double zscore = 0d;
                if (collect!=null||collect.size()!=0) {
                    //获取第一个成员(最大分数的成员)
                    zscore = redisUtil.zscore(RedisKeyUtil.talkRoomNum(roomNum), collect.get(0));
                }
                redisUtil.zincrby(RedisKeyUtil.talkRoomNum(roomNum),objectMapper.writeValueAsString(messageModel),zscore+1d);


            }else {
                //不存在的情况 新增 score 就是为了 排序的 zadd 的score 是设置初始分数
                //而 zincrby 是在上一条数据的分数 上增加 你指定的分数
                redisUtil.zadd(RedisKeyUtil.talkRoomNum(roomNum),objectMapper.writeValueAsString(messageModel),0);
            }
        }



        //session.getBasicRemote().sendText(message);

        Set<Session> sessions = roomMap.get(roomNum);

        // 给房间内所有用户推送信息
        for (Session s : sessions) {
            //当前 id 和 房间的session 的id 不相等 才推送 避免重复推送
//            if (s.getId()!=session.getId()){
//                s.getBasicRemote().sendText(message);
//            }
            s.getBasicRemote().sendText(objectMapper.writeValueAsString(messageModel));
        }
    }


    @OnError
    public void error(Throwable error) {
        log.error("链接异常中断");
        error.printStackTrace();
    }
}
